/*
兼容性和协变，逆变

兼容性： 指的是类型兼容，我要的你一定能给就可以兼容
> 1. 基本类型兼容性，
-- null 不能赋值给任何其他类型(any 和 unknown 除外)
-- 类的实例可以赋值给类

从安全性来考虑,不能从属性的多少来考虑，必须全部满足才能兼容；
联合类型因为可能有第二种属性，所以不能兼容单一属性
string 类型默认有一些方法，如 toString， 可以兼容这些属性

> 2. 接口类型兼容
你要的我一定有，就可以兼容

>3. 函数兼容，从以下两方面考虑
3.1 看函数的参数，函数返回值
-- 参数要求： 兼容赋值时， 等号 = 右边的函数参数可以少于左边函数的参数， 反之不行
   理解： 定义一个函数有三个参数，实际调用时可以只传一个，此时，参数少的可以兼容参数多的，反之不行；
   参数不能兼容，兼容的是函数类型


3.2 看函数返回值
-- 等号右边的类型，属性可以少于左边的
   理解: 可以有多余的属性，但不能我要的时候你拿不出来

>4 协变与逆变（参数可逆变，返回值可协变）
  > 本质还是：对于返回值，可多不可少; 对于参数，可少不可多
  > 继承中的属性，父比子少；
-- 协变: 函数的返回值, 可以兼容继承的子级, 可以是儿子或自己，但不能兼容继承的父级；（可多不可少）
-- 逆变: 函数的参数可以逆变, 可以兼容继承的父级，可以是父级或自己，但不能兼容击沉过的子级（可少不可多）
-- 双向协变： 非严格模式下, 参数支持双向协变, 即传子或传父都可以, 不建议使用；  strictFunctionTypes: false

>5. 类型中的父子关系协变
-- 类型多的为父，类型少的尾子, 可以将父赋值给子，反之不行
   理解： 

*/
//-------------普通类型兼容-----------------------
type person1 = any  // 子类型
type person2 = string | number | boolean // 父类型
let p1!: person1
let p2!: person2
p2 = p1  // 正确
// p1 = p2  // 错误  , 当 p2 为 boolean 时，不兼容
//-----------------------------------------------


//-----------类与类的实例互相兼容------------------
class A {
}
let a: A = A
let a1 = new A()
a = a1
// a1 = a

//--------------------------------------------


//-------------看函数返回值----------------------
type sum1 = () => string
type sum2 = () => string | number
let s1!: sum1
let s2!: sum2
s2 = s1 // 反之不行， 遵循安全规则

type su1 = () => { name: string, age: number }
type su2 = () => { name: string }
let s3!: su1
let s4!: su2
s4 = s3 // 反之不行， 遵循安全规则,属性多的可以赋值给属性少的
//-----------------------------------



// ------------------自己实现一个 forEach， 从参数考虑--------------------------
type ForEachFn<T> = (item: T, index: number) => void
function forEach<T>(arr: T[], cb: ForEachFn<T>) {
    for (let i = 0; i < arr.length; i++) {
        let item = arr[i]
        cb(item, i)
    }
}

forEach<number>([1, 3, 5, 6,], (item) => {
    console.log(item);
})

//---------------------------------------------

//-----------------逆变与协变----------------------
class Parent {
    address: string = 'zs'
}
class Child extends Parent {
    name: string = 'zs'
}
class Grandson extends Child {
    money: number = 123
}
type MyFn = (person: Child) => Child
function getFn(cb: MyFn) { }


getFn((person: Child) => new Child) // 正确
//--------------参数可逆变---------
getFn((person: Parent) => new Child) // 正确，将父赋值给子
getFn((person: Grandson) => new Child) // 错误

//--------------参数可协变---------
getFn((person: Child) => new Grandson) // 正确
getFn((person: Child) => new Parent) // 错误
//-----------------------------------------------------------------


//----------------------类型中的父子协变-------------------------------------------
type Pn = number | boolean | string // 父类型
type Ff = string | number // 子类型
type Pp = string // 孙类型

type Fn = (person: Ff) => number
function xxFn(cb: Fn) { }

xxFn((person: Pn) => 123) // 正确, 可以将 Fn 型赋值给 Pn 型, 即父类型可以赋值给子类型
xxFn((person: Pp) => 123) // 错误，不能 将 Fn 赋值给 Pp 型，孙类型不可以赋值给子类型


type pp = null
type pp2 = any

let ppp!: pp
let ppp2!: pp2
ppp2 = ppp
